package net.xdclass.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.enums.CouponStateEnum;
import net.xdclass.enums.ProductOrderStateEnum;
import net.xdclass.enums.StockTaskStateEnum;
import net.xdclass.exception.BizException;
import net.xdclass.feign.ProductOrderFeignService;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.mapper.CouponRecordMapper;
import net.xdclass.mapper.CouponTaskMapper;
import net.xdclass.model.CouponRecordDO;
import net.xdclass.model.CouponRecordMessage;
import net.xdclass.model.CouponTaskDO;
import net.xdclass.model.LoginUser;
import net.xdclass.request.LockCouponRecordRequest;
import net.xdclass.service.CouponRecordService;
import net.xdclass.service.CouponService;
import net.xdclass.utils.JsonData;
import net.xdclass.vo.CouponRecordVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CouponRecordServiceImpl implements CouponRecordService {
    @Autowired
    private CouponRecordMapper couponRecordMapper;

    @Autowired
    private CouponTaskMapper couponTaskMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private ProductOrderFeignService orderFeignService;

    @Override
    public Map<String, Object> page(int page, int size) {
        LoginUser loginUser = (LoginUser) LoginInterceptor.threadLocal.get();

        Page<CouponRecordDO> pageInfo = new Page<>(page, size);
        IPage<CouponRecordDO> recordDOIPage = couponRecordMapper.selectPage(pageInfo, new QueryWrapper<CouponRecordDO>()
                .eq("user_id", loginUser.getId())
                .orderByDesc("create_time")
        );
        Map<String, Object> pageMap = new HashMap<>(3);

        pageMap.put("total_record", recordDOIPage.getTotal());
        pageMap.put("total_page", recordDOIPage.getPages());
        pageMap.put("current_data", recordDOIPage.getRecords().stream()
                .map(obj -> beanProcess(obj))
                .collect(Collectors.toList())
        );

        return pageMap;
    }

    @Override
    public CouponRecordVO findById(int id) {
        LoginUser  loginUser = (LoginUser)LoginInterceptor.threadLocal.get();
        CouponRecordDO couponRecordDO = couponRecordMapper.selectOne(new QueryWrapper<CouponRecordDO>()
                .eq("id", id)
                .eq("user_id", loginUser.getId())
        );

        if (couponRecordDO == null) {
            return null;
        }

        return beanProcess(couponRecordDO);
    }

    @Override
    public JsonData lockCouponRecords(LockCouponRecordRequest recordRequest) {
        LoginUser loginUser = (LoginUser) LoginInterceptor.threadLocal.get();
        String orderOutTradeNo = recordRequest.getOrderOutTradeNo();
        List<Long> lockCouponRecordIds = recordRequest.getLockCouponRecordIds();

        int updateRow = couponRecordMapper.lockUsesStateBatch(loginUser.getId(), CouponStateEnum.USED.name(), lockCouponRecordIds);
        List<CouponTaskDO> couponTaskDOList = lockCouponRecordIds.stream()
                .map(item -> {
                    CouponTaskDO couponTaskDO = new CouponTaskDO();
                    couponTaskDO.setCreateTime(new Date());
                    couponTaskDO.setLockState(StockTaskStateEnum.LOCK.name());
                    couponTaskDO.setOutTradeNo(orderOutTradeNo);
                    couponTaskDO.setCouponRecordId(item);
                    return couponTaskDO;
                }).collect(Collectors.toList());

        int insertRow = couponTaskMapper.insertBatch(couponTaskDOList);
        log.info("优惠券更新記錄: {}", updateRow);
        log.info("优惠券鎖定記錄: {}", insertRow);

        if (lockCouponRecordIds.size() == updateRow && couponTaskDOList.size() == insertRow) {
            couponTaskDOList.stream()
                    .forEach(item -> {
                        CouponRecordMessage couponRecordMessage = new CouponRecordMessage();
                        couponRecordMessage.setOutTradeNo(item.getOutTradeNo());
                        couponRecordMessage.setTaskId(item.getId());

                        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getCouponReleaseDelayRoutingKey(), couponRecordMessage);
                        log.info("优惠券鎖定消息發送成功: {}", couponRecordMessage.toString());
                    });

            return JsonData.buildSuccess();
        } else {
            throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public boolean releaseCouponRecord(CouponRecordMessage couponRecordMessage) {
        CouponTaskDO taskDO = couponTaskMapper.selectOne(new QueryWrapper<CouponTaskDO>().eq("id", couponRecordMessage));

        if (taskDO == null) {
            log.warn("工作單不存在, 消息: {}", couponRecordMessage);
            return true;
        }

        if (taskDO.getLockState().equalsIgnoreCase(StockTaskStateEnum.LOCK.name())) {
            JsonData jsonData = orderFeignService.queryProductOrderState(couponRecordMessage.getOutTradeNo());
            if (jsonData.getCode() == 0) {
                String state = jsonData.getData().toString();
                if (ProductOrderStateEnum.NEW.name().equalsIgnoreCase(state)) {
                    log.warn("訂單狀態是NEW, 返回給消息隊列, 重新投遞: {}", couponRecordMessage);
                    return false;
                }

                if (ProductOrderStateEnum.PAY.name().equalsIgnoreCase(state)) {
                    taskDO.setLockState(StockTaskStateEnum.FINISH.name());
                    couponTaskMapper.update(taskDO, new QueryWrapper<CouponTaskDO>().eq("id", couponRecordMessage.getTaskId()));
                    log.info("訂單已經支付, 修改庫存鎖定工作單FINISH狀態: {}", couponRecordMessage);
                    return true;
                }
            }

            log.warn("訂單不存在, 或者訂單被取消, 确認消息, 修改task狀態為CANCEL, 恢复优惠券使用記錄為NEW, message: {}", couponRecordMessage);
            taskDO.setLockState(StockTaskStateEnum.CANCEL.name());
            couponTaskMapper.update(taskDO, new QueryWrapper<CouponTaskDO>().eq("id", couponRecordMessage.getTaskId()));
            couponRecordMapper.updateState(taskDO.getCouponRecordId(), CouponStateEnum.NEW.name());

            return true;
        } else {
            log.warn("工作單不是LOCK, state={}, 消息體={}", taskDO.getLockState(), couponRecordMessage);
            return true;
        }
    }

    private CouponRecordVO beanProcess(CouponRecordDO obj) {
        CouponRecordVO couponRecordVO = new CouponRecordVO();
        BeanUtils.copyProperties(obj, couponRecordVO);
        return couponRecordVO;
    }
}
